#include <iostream>
#include "../include/socket.h"
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include "../include/conf_reader.h" //用于读取配置文件
#include "../include/my_log.h"
#include "../include/macro.h"
#include <fcntl.h>
#include <stdlib.h>
#include <cerrno>
#include <stdio.h>
#include "../include/global.h"
#include <string.h>
#include "../include/crc32.h"
_socket::_socket()
{
    // 一些潜在的需要初始化的变量
    this->total_connections_num=0;
    this->free_connections_num=0;
    this->recycle_connection_num=0;
    this->listen_fd_num=1;

    this->read_conf();  //读配置放到这里

}
//初始化监听端口
bool _socket::init_listening_socket()
{
    //初始化监听套接字
    my_log * log=my_log::get_log_instance();
    conf_reader * reader=conf_reader::get_conf_reader_instance();
    this->listen_fd_num=atoi(reader->get_value_from_key("listen_fd_num",DEFAULT_NUM_LISTEN_FD));  //读取配置文件中的监听套接字数量
    
    //用于绑定的监听套接字结构
    struct sockaddr_in addr;
    int sock_len=sizeof(addr);
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=INADDR_ANY;    //监听任意到达本机的请求

    for(int i=0;i<listen_fd_num;++i){
        int fd=socket(AF_INET,SOCK_STREAM,0);
        if(fd==-1){
            log->write_log(LOG_EMERG,errno,"监听套接字创建失败");
            return false;
        }
        //设置监听套接字可以地址重用
        int is_set=1;    
        if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&is_set,sizeof(is_set))==-1){
            log->write_log(LOG_ALERT,errno,"设置地址重用失败");
            return false;
        }
        //设置端口复用，当用于多进程的时候，能够解决惊群问题
        if(setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,&is_set,sizeof(is_set))==-1){
            log->write_log(LOG_ALERT,errno,"设置端口重用失败");
            return false;
        }
        //设置监听端口为非阻塞，配合边缘触发，效率高
        if(this->set_no_blocking(fd)==false){
            //里面通过调用fcntl设置失败会设置errno
            log->write_log(LOG_ALERT,errno,"设置端口重用失败");
            return false;
        }
        //读取端口
        char info_port[20];
        snprintf(info_port,20,"listen_port%d",i);
        int now_port=atoi(reader->get_value_from_key(info_port,"-1"));
        my_log::get_log_instance()->write_log(LOG_INFO,0,"成功监听%d",now_port);
        if(now_port==-1){
            log->write_log(LOG_ALERT,errno,"从配置文件中获取端口失败，检查配置文件");
            return false;
        }
        addr.sin_port=htons(now_port);
        //绑定套接字
        if(bind(fd,(struct sockaddr *)&addr,sock_len)==-1){
            log->write_log(LOG_ALERT,errno,"使用bind绑定监听套接字失败");
            return false;
        }
        if(listen(fd,LISTEN_BACK_QUEUE)==-1){
            log->write_log(LOG_ALERT,errno,"使用listen监听待连接队列套接字失败");
            return false;
        }
        //创建一个自定义的监听结构体
        listenting_p p=new listenting_d;
        p->fd=fd;
        p->port=now_port;
        p->connection=nullptr;
        //放入连接队列即可
        this->listening_list.push_back(p);  //等待后续取出来，与连接池做绑定

        //-----------------------------------------以上都没有close(fd)该生命周期本来就与程序绑定，程序退出后，让操作系统帮忙close吧！
    }
    
    log->write_log(LOG_INFO,0,"成功创建%d个监听端口",listen_fd_num);
    return true;

}

bool _socket::set_no_blocking(int fd)
{
    int flags=fcntl(fd,F_GETFL,0);
    if(flags==-1){
        return false;
    }
    flags|=O_NONBLOCK;
    if(fcntl(fd,F_SETFL,flags)==-1){
        return false;
    }

    return true;
}

void _socket::read_conf()
{
    conf_reader * reader=conf_reader::get_conf_reader_instance();
    this->work_connections=atoi(reader->get_value_from_key("worker_connections","2048")); //读取允许的客户端连接数
    //读取连接回收的等待时间
    this->recycle_time=atoi(reader->get_value_from_key("Sock_RecyConnectionWaitTime",DEFAULT_WAIT_RECYCLE_TIME));   //延迟回收的时间
    this->is_start_kick_map=atoi(reader->get_value_from_key("Sock_WaitTimeEnable","0"));    // 是否开启踢人
    printf("is_start_kick_map =%d\n",is_start_kick_map);
    if(is_start_kick_map==1){
        printf("已开启踢人\n");
        this->time_of_check_gap=atoi(reader->get_value_from_key("Sock_MaxWaitTime","20"));      // 踢人的gap
        this->is_force_to_kick=atoi(reader->get_value_from_key("Sock_ForceKick","0"));  //是否强制踢人
        this->time_of_force_kick=180;   //强制踢出时间为3min
    }
   
    this->num_of_thread=atoi(reader->get_value_from_key("ProcMsgRecvWorkThreadCount","30"));    //线程数量
    
    
    
}

int _socket::doing_send(int fd,char*buffer,int len)
{
    while(1){
        int send_res=send(fd,buffer,len,0);
        // std::cout<<"这里睡了10s此次发送需要发送："<<len<<"真实发送,"<<send_res<<std::endl;
        // sleep(10);
        if(send_res>0){
            return send_res;
        }

        if(send_res==0){
            return 0;           //返回0是什么意思---几乎不可能
        }

        if(errno==EAGAIN||errno==EWOULDBLOCK){
            //这表示缓冲区满了
            return -1;
        }
        
        //发送出错了
        if(errno==EINTR){
            //被信号中断,可以待会再发
            my_log *log=my_log::get_log_instance();
            log->write_log(LOG_ERR,errno,"发送消息失败了，被信号中断了");
            continue;
        }

        

        return -2;      //基本表示对端可能关闭了

    }
    


    
}

int _socket::change_epoll_event(int fd, uint32_t new_event,connection_p con_p)
{
    struct epoll_event event;
    event.events=new_event;
    event.data.ptr=con_p;

    if(epoll_ctl(epoll_fd,EPOLL_CTL_MOD,fd,&event)==-1){
        my_log *log=my_log::get_log_instance();
        log->write_log(LOG_ERR,errno,"缓冲区满了之后或者发送完缓冲区有空闲后的逻辑 epoll_ctl failed");
        return -1;
    }

    return 0;
}

void _socket::clear_send_message_list()
{
    //这个函数是在发送线程结束后执行的，所以没有加锁的必要------执行这些函数前，需要先把处理线程停掉
    while(!this->send_message_list.empty()){

        auto data=send_message_list.front();
        delete[] data;
        send_message_list.pop_front();

    }   
    printf("清空发送队列完毕\n");

}

//发送消息的线程体
void *_socket::send_message_to_client_thread(void *thread_item)
{
    thread_item_for_socket * item=(thread_item_for_socket *)(thread_item);
    _socket * external_socket=item->external_socket;
    std::list<char *>tmp_buf_full_message;
    // 表示线程在运行中
    while(1){
        // // 4/21 还是想把采用信号量机制，改为使用条件变量
        // if(sem_wait(&external_socket->send_message_sem)==-1){
        //     //被信号中断了
        //     if(errno==EINTR){
        //         my_log * log=my_log::get_log_instance();
        //         log->write_log(LOG_ERR,errno,"send_message_thread  interrupted by signal");
        //     }
        //     //也让它走下去

        // }
        // std::cout<<"被唤醒了"<<std::endl;
         //上锁，拿数据了
        
        if(pthread_mutex_lock(&external_socket->send_message_mutex)!=0){
            //发生概率极低
            my_log * log=my_log::get_log_instance();
            log->write_log(LOG_ERR,0,"send_message_thread  lock failed");
            continue;
        }

        while(external_socket->send_message_list.empty()&&external_socket->send_message_thread_running_flag==PRO_RUNNING){
            //std::cout<<"成功睡觉"<<std::endl;
            pthread_cond_wait(&external_socket->send_thread_cond,&external_socket->send_message_mutex);
        }
        // std::cout<<"开始睡5s"<<std::endl;
        // sleep(5);
        // std::cout<<"5s睡完，起床"<<std::endl;
        if(external_socket->send_message_list.empty()&&external_socket->send_message_thread_running_flag==PRO_END){    //程序结束标志
            break;
        }
       
        //循环处理
        
        tmp_buf_full_message.clear();
        while(!external_socket->send_message_list.empty()){
            //每次拿取头部
            std::cout<<"进入循环了"<<std::endl;
            
            char *buffer=external_socket->send_message_list.front();
            external_socket->send_message_list.pop_front();

            //拿取消息头
            message_head_d * head=(message_head_d *)buffer;
            
            //判断该包是否有效
            if(head->con_p->icurrentsequence!=head->iCurrSequence){
                //是废包
                delete[] buffer;

                continue;
            }
           
            //判断缓冲区状态
            if(head->con_p->send_buf_is_full_flag==SEND_BUF_FULL){
                //暂时存放一下该数据
                tmp_buf_full_message.push_back(buffer);
                continue;
            }
           
            //然后拿取到消息头与包头 进行发送
            int fd=head->con_p->fd;
            package_head_d * pkg_head=(package_head_d *)(buffer+sizeof(message_head_d));    //包头
            int need_send_len=ntohs(pkg_head->pkg_body_len)+sizeof(package_head_d); //包头加包体
            //std::cout<<"执行发送"<<"fd:"<<fd<<"need_send_len:"<<need_send_len<<std::endl;
            char*need_send_pos=buffer+sizeof(message_head_d);
            int send_res=external_socket->doing_send(fd,need_send_pos,need_send_len);
            //std::cout<<"执行完发送动作"<<std::endl;
            if(send_res>0){
                if(send_res==need_send_len){
                    (head->con_p->wait_to_send_count)--;
                    (external_socket->send_message_list_size)--;
                    delete[] buffer;    //发送完毕
                }else{
                    //发送缓冲区满了，放入套接字结构体里，等待epoll_wait触发发送
                    need_send_len-=send_res;    //改变长度
                    head->con_p->send_buf_is_full_flag=SEND_BUF_FULL;   //标记为满了

                    //将该fd对应的事件添加EPOLLOUT
                    if(external_socket->change_epoll_event(fd,EPOLLIN|EPOLLOUT|EPOLLRDHUP,head->con_p)==-1){
                        //如果返回-1，里面写日志了，那么直接丢弃该包，不管了-------------------
                        //delete[] buffer;
                    }
                    head->con_p->sendbuf=buffer+sizeof(message_head_d)+send_res;
                    head->con_p->sendlen=need_send_len; //上面计算了
                    head->con_p->send_message_ptr=buffer; //// 拿到整条消息句柄，用于释放
                    (external_socket->send_message_list_size)--;
                    //还没有发送完毕所以 不需要  --(head->con_p->wait_to_send_count);
                    
                }
                
                continue;
            }else if(send_res==-1){
                //发送返回-1,走到这里缓冲区满了，但是之前的却没有标记，说明，之前调用send后刚满，那么此时也需要执行像上面发送不完全的逻辑
                //发送缓冲区满了，放入套接字结构体里，等待epoll_wait触发发送
                
                head->con_p->send_buf_is_full_flag=SEND_BUF_FULL;   //标记为满了
                if(external_socket->change_epoll_event(fd,EPOLLIN|EPOLLOUT|EPOLLRDHUP,head->con_p)==-1){
                    //如果返回-1，那么直接丢弃该包，不管了------发生的概率也是极低的
                    //delete[] buffer;
                }
                head->con_p->sendbuf=buffer+sizeof(message_head_d);
                head->con_p->sendlen=need_send_len; //上面计算了（包头加包体）
                head->con_p->send_message_ptr=buffer;   // 拿到整条消息句柄，用于释放
                --(external_socket->send_message_list_size);
                //还没有发送完毕所以 不需要  --(head->con_p->wait_to_send_count);

            }else if(send_res==0 || send_res==-2){
                //缓冲区没满+至少有包头数据    居然返回0/-2，基本不会发生
                //如果发生  对端可能关闭了
                delete[] buffer;
                --(external_socket->send_message_list_size);
            }


        }
        //再往里加上暂时发不出去的数据
        if(tmp_buf_full_message.empty()==false){
            //不为空
            external_socket->send_message_list=move(tmp_buf_full_message);
            tmp_buf_full_message.clear();
        }
        
        std::cout<<"发送完了"<<std::endl;


         //解锁，数据处理完了
         if(pthread_mutex_unlock(&external_socket->send_message_mutex)!=0){
            //发生概率极低,或者说这里就不会失败，因为上锁成功了
            my_log * log=my_log::get_log_instance();
            log->write_log(LOG_ERR,0,"send_message_thread  unlock failed");
            continue;
        }
        

    }
    


    return NULL;
}

void _socket::move_message_to_send_list(connection_p con_p,const char *body,unsigned short body_len,unsigned short pkg_type){
    //将数据包组装好，放到待发送队列里
    int msg_len=sizeof(message_head_d)+sizeof(package_head_d)+body_len;
    char *msg=new char[msg_len];
    memset(msg,0,msg_len);
    //消息头
    message_head_d * msg_head=(message_head_d *)(msg);
    msg_head->con_p=con_p;
    msg_head->iCurrSequence=con_p->icurrentsequence;

    int needed_crc32=CCRC32::GetInstance()->Get_CRC((unsigned char *)body,body_len);
    //偏移一下，包头
    package_head_d * pkg_head=(package_head_d *)(msg+sizeof(message_head_d));
    pkg_head->crc32=htonl(needed_crc32);
    pkg_head->pkg_body_len=htons(body_len);
    pkg_head->pkg_type=htons(pkg_type);
    char *msg_body=(msg+(msg_len-body_len));
    memcpy(msg_body,body,body_len);
    //组装完毕，放入，并通知即可，但是要加锁
    bool  is_enable_send_flag=true;
    pthread_mutex_lock(&send_message_mutex);

    //考不考虑，服务器里待发送队列的数据里数据条数过多，或者该链接堆积的消息过多
    if(send_message_list_size>SEND_LIST_MAX_SIZE){
        //服务器里堆积的数据已经够多了，就把这条数据丢了吧
        my_log::get_log_instance()->write_log(LOG_ALERT,0,"当前服务器堆积的发送消息过多，丢弃一条");
        delete[] msg;
        is_enable_send_flag=false;
    }else{
        if(con_p->wait_to_send_count>=CON_WAIT_SEND_MAX_SIZE){
            //这样，也是判断这个连接是恶意的，强制断开
            my_log::get_log_instance()->write_log(LOG_ALERT,0,"当前连接堆积的发送消息过多，丢弃一条");
            add_con_to_recycle(con_p);
            delete[] msg;
            is_enable_send_flag=false;
        }
    }
    if(is_enable_send_flag){
        send_message_list.push_back(msg);
        con_p->wait_to_send_count++;
        this->send_message_list_size++;
    }
    

    pthread_mutex_unlock(&send_message_mutex);
    
    pthread_cond_signal(&this->send_thread_cond);  
    std::cout<<"放入发送队列成功"<<std::endl;
    
}

_socket::~_socket()
{
}

void _socket::thread_for_deal_message(char *message)
{
    //放在这里，子类实现
}